---
title: UIScript 的使用和绑定
---

开发者使用编辑器对自定义材质属性进行调节的同时，还可以通过 `UIScript` 指令指定数据变更的回调行为。通过 UIScript 暴露的 hook 函数，开发者可以实现属性联动，从而减少声明属性的数量，达到简化 Inspector 面板的目的。

### 在 ShaderLab 中绑定 `UIScript`:

```glsl showLineNumbers
Editor {
    ...
    UIScript "/path/to/script";
    ...
}
```

绑定的`UIScript`脚本路径支持相对路径和绝对路径，以下图项目根目录为例，绝对路径为
`/PBRScript1.ts`，相对路径为`./PBRScript1.ts`

<Image
  src="https://mdn.alipayobjects.com/huamei_aftkdx/afts/img/A*t4LFQ4KEL6kAAAAAAAAAAAAADteEAQ/fmt.webp"
  width="70%"
/>

### UIScript 接口

编辑器通过内置 `ShaderUIScript` 类暴露相关 API，`ShaderUIScript` 的类型定义已经内嵌至 Galacean Web 端脚本编辑器中，完整定义如下：

```ts
import { Color, Material, Texture, Vector2, Vector3, Vector4 } from "@galacean/engine";

type ShaderPropertyValue = number | Vector2 | Vector3 | Vector4 | Color | Texture;
type ShaderMacroValue = number | Vector2 | Vector3 | Vector4 | Color;

/**
 * Script for extending `Shader` UI logic.
 */
export abstract class ShaderUIScript {
  /** @internal */
  _propertyCallBacks: Map<string, (material: Material, value: ShaderPropertyValue) => void> = new Map();

  /** @internal */
  _macroCallBacks: Map<string, (material: Material, defined: boolean, value: ShaderMacroValue) => void> = new Map();

  /**
   * The method is called when then shader is switched.
   * @param material - The material which the shader is bound to
   */
  onMaterialShaderSwitched(material: Material): void {}

  /**
   * Register property change callback.
   * @parma propertyName - Property name
   * @parma onChanged - Fired on property changed
   */
  protected onPropertyChanged(
    propertyName: string,
    onChanged: (material: Material, value: ShaderPropertyValue) => void
  ): void {
    this._propertyCallBacks.set(propertyName, onChanged);
  }

  /**
   * Register macro change callback.
   * @parma macroName - Macro name
   * @parma onChanged - Fired on macro changed
   */
  protected onMacroChanged(
    macroName: string,
    onChanged: (material: Material, defined: boolean, value: ShaderMacroValue) => void
  ): void {
    this._macroCallBacks.set(macroName, onChanged);
  }
}
```

### UIScript 编写

1. 在编辑器中创建 UIScript 脚本

<Image
  src="https://mdn.alipayobjects.com/huamei_aftkdx/afts/img/A*Qh4UTZgaY7MAAAAAAAAAAAAADteEAQ/fmt.webp"
  width="60%"
  figcaption="创建UIScript"
/>

2. 通过继承 ShaderUIScript 类指定属性变更回调。

```ts
import { Material, RenderQueueType, Vector3, BlendFactor, RenderFace, CullMode, BlendMode } from "@galacean/engine";

export default class extends ShaderUIScript {
  constructor() {
    super();

    ......
    // 在构造函数中注册监听属性回调
    this.onPropertyChanged("material_NormalTexture", (material: Material, value) => {
      const shaderData = material.shaderData;
      if (value) {
        shaderData.enableMacro("MATERIAL_HAS_NORMALTEXTURE");
      } else {
        shaderData.disableMacro("MATERIAL_HAS_NORMALTEXTURE");
      }
    })

    ......

  }

  // 指定 shader 绑定的回调
  override onMaterialShaderSwitched(material: Material) {
    const shaderData = material.shaderData;

    shaderData.disableMacro("MATERIAL_OMIT_NORMAL");
    shaderData.enableMacro("MATERIAL_NEED_WORLD_POS");
    shaderData.enableMacro("MATERIAL_NEED_TILING_OFFSET");

    // default value
    const anisotropyInfo = shaderData.getVector3("material_AnisotropyInfo");

    if (!anisotropyInfo) {
      shaderData.setVector3("material_AnisotropyInfo", new Vector3(1, 0, 0));
    } else {
      shaderData.setFloat("anisotropy", anisotropyInfo.z);
      const PI2 = Math.PI * 2;
      const rotationRad = (Math.atan2(anisotropyInfo.y, anisotropyInfo.x) + PI2 ) % PI2;
      shaderData.setFloat("anisotropyRotation", rotationRad * (180 / Math.PI))
    }
  }
}

```

<Callout info="warning">
  注意，当前版本 ShaderLab 材质属性模块只是定义了绑定该 Shader 的材质在编辑器中的 Inspector
  UI面板，并不会替你在`ShaderPass`中声明对应的全局变量，如果`ShaderPass`代码中引用了该变量需在[全局变量](./shader/#全局变量)模块中明确声明补充。
</Callout>